#pragma once;

/**
 * The EffectParameter class encompasses the Parameters for both 
 * the shared parameters used by the cgFX runtime and by 
 * individual effects.
 */

#include "Object.h"

class EffectParameter : public Object
{
public:
    enum ParameterVariability
    {
        // Uniform properties can be modified without needing a recompile.
        PV_Uniform = CG_UNIFORM,
        // If a parameters variablility is changed to Literal, the effect
        // will need to be recompiled.
        PV_Literal = CG_LITERAL,
        // Setting the parameters variablity to default will revert the parameter
        // to it's default variability.
        PV_Default = CG_DEFAULT,
    };

    // A static instance that is reserved for returning an invalid 
    // parameter.  Effect parameters should be checked for validity 
    // before being used.
    static EffectParameter InvalidEffectParameter;

    const std::string& GetParameterName() const;
    const std::string& GetParameterSemantic() const;

    // Values can only be set on primitive types:
    // - primitive scalars of type bool, int, float, or double
    // - primitive vectors of size 2, 3, and 4
    // - primitive matrices of size 2x2, 3x3, and 4x4.
    // If you want to extend the allowed types, see EffectParameterSpecialization.cpp
    // for an example on how to provide specializations for different vector or matrix types.
    // Specializations for glm vector and matrix types has been provided.
    template< typename T >
    void Set( const T& value );

    // Get the internal value of the parameter
    // Valid parameter types are:
    //  Pointers to primitives:
    //  - bool*, int*, float*, double*,
    //  TextureID for Texture parameters
    //  - GLuint
    // This method will throw an exception of type boost::bad_any_cast
    // if the internal type could not be cast to the requested type.
    template< typename T >
    const T& Get() const;

    // Check to see if this parameter (or any nested parameter)
    // needs to be updated in the Cg runtime.
    // Returns true if this parameter needs to be updated with UpdateParameter().
    bool NeedsUpdate() const;

    // If the parameter has been modified, it needs to be assigned 
    // back to the runtime in order to be applied to the shader
    // The user can do this manually by calling this function, or 
    // the Effect class will ensure that changed parameters are updated 
    // before using the shader.
    bool UpdateParameter();

    // Get a nested parameter by name (in case we're a struct)
    EffectParameter& operator[]( const std::string& name );
    // Get a nested parameter by index (in case we're an array)
    EffectParameter& operator[]( size_t index );

    // Set the variability of the parameter
    void SetVariability( ParameterVariability variability );

    // Return true if the parameter is a valid parameter.
    bool IsValid() const;

    // Return true if the parameter is the default value as specified 
    // in the shader file.
    // Returns false if this parameter has ever been modified by the 
    // user (at runtime)
    bool IsDefault() const;

    // Returns true if a semantic has been assigned to this parameter.
    bool HasSemantic() const;

protected:
    // Retrieves the internal value of the parameter and stores it in 
    // the m_ParameterValue member variable.
    // Iterates nested parameters in the case of arrays or structs
    // and stores the nested parameters in the m_NestedParameters array.
    virtual void GetInternalValue();

    // Try to set the internal value based on the parameter class
    virtual void SetValue( const boost::any& value );

    virtual void SetBoolValue( const bool* value, size_t size );
    virtual void SetIntValue( const int* value, size_t size );
    virtual void SetFloatValue( const float* value, size_t size );
    virtual void SetDoubleValue( const double* value, size_t size );

    virtual void SetSampler( const boost::any& value );
    virtual void SetObject( const boost::any& value );

    // Delete any dynamic memory allocated by the parameter
    virtual void Destroy();

private:
    // Some parameters might have nested types (like structs and arrays).
    typedef std::vector< EffectParameter* > ParameterList;
    // A name, parameter map for faster lookup
    typedef std::map< std::string, EffectParameter* > ParameterMap;

    friend class EffectManager;
    friend class Effect;
    // Parameters cannot be created directly.
    // They must be retrieved from the Effect
    EffectParameter( CGcontext context, CGparameter parameter );
    virtual ~EffectParameter();

    // Effects can check if a recompile is necessary
    // Returns true if a parameter has been set to Literal
    // or a Literal parameter has been modified.
    bool NeedsRecompile() const;

    CGcontext           m_cgContext;
    CGparameter         m_cgParameter;
    
    CGparameterclass    m_cgParameterClass;
    CGtype              m_cgType;
    CGtype              m_cgBaseType;
    CGenum              m_cgVariability;

    // For nested parameter types( arrays and structs ) we need to provide
    // access to the internal parameters.  We do this with with the 
    // composite design pattern.
    ParameterList       m_NestedParameters;
    ParameterMap        m_NestedParametersByName;

    std::string         m_ParameterName;
    std::string         m_ParameterSemantic;

    boost::any          m_ParameterValue;
    // For vector or matrix types, the number of rows/columns will determine the internal type.
    int                 m_iNumRows;
    int                 m_iNumCols;
    
    // Is this a valid parameter?
    bool                m_bIsValid;

    // Has the internal value of the parameter changed?
    // If so, we need to rebind the parameter value,
    // for literal values we may need to recompile the effect.
    bool                m_bIsDirty;
    // If the parameter has been modfied since it was loaded, 
    // the parameter may not be it's default value anymore.
    // If the effect is reloaded, and the parameter has been modified,
    // then we have to set the newly loaded parameter to the modified value
    // so we don't have to manually apply the parameter again.
    bool                m_bIsDefault;
};

// Template Specialization definitions 
#include "EffectParamaterSpecialization.inl"
// Non-specialized template definitions
#include "EffectParameter.inl"